/*
 * 文件名： ScanPayMerchantServerRes.java
 * 
 * 工程名称: bis-web
 *
 * Gopay
 *
 * 创建日期： 2017年4月6日
 *
 * Copyright(C) 2017, by www.gopay.com.cn Inc.All rights reserved.
 *
 * 原始作者: 宣广海
 *
 */
package com.gopay.bis.web.controller.scan;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.PrivateKey;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.w3c.dom.Document;

import com.gopay.bis.service.bank.BankIntrChannConfService;
import com.gopay.bis.web.controller.util.CryptoUtil;
import com.gopay.common.constants.bank.BankConstants;
import com.gopay.common.constants.bank.BisBankBusinessProd;
import com.gopay.common.constants.bank.BisBankInterfaceChannel;
import com.gopay.common.constants.proccode.ProcCodeConstants;
import com.gopay.common.domain.bank.BaseMessage;
import com.gopay.common.domain.bank.wechat.scan.cmbc.Head;
import com.gopay.common.domain.bank.wechat.scan.cmbc.MerchantSettled;
import com.gopay.common.domain.bank.wechat.scan.cmbc.TransMsg;
import com.gopay.common.domain.bis.BaseBisBankExtConfig;
import com.gopay.common.exception.GopayException;
import com.gopay.common.util.HttpTransport;
import com.gopay.remote.bank.businesses.CmbcScanPayService;

/**
 * 商户入驻接受异步通知
 *
 * @author 宣广海
 *
 * @version
 *
 * @since 2017年4月6日
 */
@Controller
public class ScanPayMerchantServerRes {
	public static final String AES_ENCRYPT = "AES/ECB/PKCS5Padding";
	public static final String RSA_ENCRYPT = "RSA/ECB/PKCS1Padding";
	public static final String CHARSET = "UTF-8";

	/**
	 * 定义日志对象
	 */
	private static Log logger = LogFactory.getLog(WechatScanCmbcServerRes.class);

	@Resource(name = "bankIntrChannConfService")
	private BankIntrChannConfService bankIntrChannConfService;
	@Resource(name = "cmbcWechatScanPayServiceClient")
	private CmbcScanPayService cmbcWechatScanPayServiceClient;
	@Resource(name = "notifyConsole")
	private HttpTransport notifyConsole;

	@RequestMapping(value = "/scanPayMerchantServerRes")
	protected void execute(HttpServletRequest request, HttpServletResponse response) {
		logger.error("---------------ScanPayMerchantServerRes  start ---------------");
		/**
		 * 打印请求参数
		 */
		displayRequestParameters(request, logger);
		/**
		 * 获取银行接口通道配置信息
		 */
		BaseBisBankExtConfig bankConfig = bankIntrChannConfService.findBaseBisBankExtConfigByBankChannel(
				BisBankBusinessProd._16, BisBankInterfaceChannel._17, BankConstants.BANK_LOGO_WECHAT);

		Map<String, String> param = new HashMap<String, String>();
		/**
		 * 解密并且验签
		 */
		try {
			TransMsg<MerchantSettled> resMessage = messageverify(request, bankConfig);
			if (resMessage != null) {
				if ("S".equals(resMessage.getHead().getRespType())) {
					param.put("merchantId", resMessage.getBody().getMerchantId());
					param.put("idCard", resMessage.getBody().getIdCard());
					param.put("payWay", resMessage.getBody().getPayWay());
					param.put("merchantCode", resMessage.getBody().getMerchantCode());
					param.put("interChann", BisBankInterfaceChannel._17.value);
					param.put("orderStatus", "S");
				}else if("E".equals(resMessage.getHead().getRespType())){					
					param.put("merchantId", resMessage.getBody().getMerchantId());
					param.put("idCard", resMessage.getBody().getIdCard());
					param.put("payWay", resMessage.getBody().getPayWay());
					param.put("interChann", BisBankInterfaceChannel._17.value);
					param.put("orderStatus", "E");
				}
			}
			/**
			 * 通知console
			 */
			notifyConsole.submit(param);
			/**
			 * 通知银行
			 */
			notifyBank(response);
		} catch (GopayException e) {
			logger.error("messageverify error：", e);
		} catch (Exception e) {
			logger.error("BankResBase error：", e);
		}
	}

	/**
	 * 
	 * 功能描述：输出异步通知请求参数，商户入驻支付返回的request获取输入流，然后输出通知参数
	 *
	 * @param request
	 * @param logger
	 * 
	 * @author 宣广海
	 *
	 * @since 2016年8月9日
	 *
	 * @update:[变更日期YYYY-MM-DD][更改人姓名][变更描述]
	 */
	@SuppressWarnings("rawtypes")
	private void displayRequestParameters(HttpServletRequest request, Log logger) {
		for (Enumeration enumeration = request.getParameterNames(); enumeration.hasMoreElements();) {
			String eleName = (String) enumeration.nextElement();
			String eleValue = request.getParameter(eleName);
			if (logger != null) {
				logger.error("map key: [" + eleName + "]，value: [" + eleValue + "]");
			}
		}
	}

	/**
	 * 
	 * 功能描述：解密并验签报文
	 *
	 * @param request
	 * @param bankConfig
	 * @return
	 * @throws Exception
	 * 
	 * @author 宣广海
	 *
	 * @since 2017年4月6日
	 *
	 * @update:[变更日期YYYY-MM-DD][更改人姓名][变更描述]
	 */
	private TransMsg<MerchantSettled> messageverify(HttpServletRequest request, BaseBisBankExtConfig bankConfig)
			throws Exception {
		TransMsg<MerchantSettled> result = null;
		String resEncryptData = request.getParameter("encryptData");
		String resEncryptKey = request.getParameter("encryptKey");
		String signData = request.getParameter("signData");
		// String reqMsgId = request.getParameter("reqMsgId");
		PrivateKey hzfPriKey = null;
		byte[] merchantXmlDataBytes = null;
		TransMsg message = new TransMsg();
		request.setCharacterEncoding(CHARSET);

		hzfPriKey = CryptoUtil.getRSAPrivateKeyByFileSuffix(bankConfig.getIntrMerPravkeyPath(), "pem", null, "RSA");
		byte[] decodeBase64KeyBytes = Base64.decodeBase64(resEncryptKey.getBytes(CHARSET));
		// 解密encryptKey得到merchantAESKey
		byte[] merchantAESKeyBytes = CryptoUtil.RSADecrypt(decodeBase64KeyBytes, hzfPriKey, 2048, 11, RSA_ENCRYPT);
		// 使用base64解码民生请求报文
		byte[] decodeBase64DataBytes = Base64.decodeBase64(resEncryptData.getBytes(CHARSET));
		// 用解密得到的merchantAESKey解密encryptData
		merchantXmlDataBytes = CryptoUtil.AESDecrypt(decodeBase64DataBytes, merchantAESKeyBytes, "AES", AES_ENCRYPT,
				null);
		String plainText = new String(merchantXmlDataBytes, CHARSET);
		logger.info("------cmbc notify plainText :" + plainText);
		/**
		 * 验签
		 */
		message.setPublicKeyPath(bankConfig.getIntrBankPubkeyPath());
		message.setSignMessage(signData);
		message.setPlainMessage(plainText);
		boolean falg = (boolean) cmbcWechatScanPayServiceClient.verify(message);
		if (falg) {
			/**
			 * 构建需要上送的对象
			 */
			result = generateResMessage(plainText);
		} else {
			throw new GopayException(ProcCodeConstants.PROC_CODE_100E7026);
		}
		return result;
	}

	/**
	 * 
	 * 功能描述：
	 *
	 * @param plainText
	 * @return
	 * @throws Exception
	 * 
	 * @author 宣广海
	 *
	 * @since 2017年4月6日
	 *
	 * @update:[变更日期YYYY-MM-DD][更改人姓名][变更描述]
	 */
	private TransMsg<MerchantSettled> generateResMessage(String plainText) throws Exception {
		Head head = new Head();
		MerchantSettled body = new MerchantSettled();
		TransMsg<MerchantSettled> resMessage = new TransMsg<MerchantSettled>();
		// 解析文件，生成document对象
		DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		InputStream inputStream = new ByteArrayInputStream(plainText.getBytes());
		Document document = builder.parse(inputStream);
		// 生成XPath对象
		XPath xpath = XPathFactory.newInstance().newXPath();
		generateResHeadMessage(document, xpath, head);
		String merchantId = (String) xpath.evaluate("/message/body/merchantId/text()", document, XPathConstants.STRING);
		String merchantCode = (String) xpath.evaluate("/message/body/merchantCode/text()", document,
				XPathConstants.STRING);
		String payWay = (String) xpath.evaluate("/message/body/payWay/text()", document, XPathConstants.STRING);
		String idCard = (String) xpath.evaluate("/message/body/idCard/text()", document, XPathConstants.STRING);
		String extend1 = (String) xpath.evaluate("/message/body/extend1/text()", document, XPathConstants.STRING);
		String extend2 = (String) xpath.evaluate("/message/body/extend2/text()", document, XPathConstants.STRING);
		String extend3 = (String) xpath.evaluate("/message/body/extend3/text()", document, XPathConstants.STRING);

		body.setMerchantId(merchantId);
		body.setIdCard(idCard);
		body.setPayWay(payWay);
		body.setMerchantCode(merchantCode);
		body.setExtend1(extend1);
		body.setExtend2(extend2);
		body.setExtend3(extend3);
		resMessage.setHead(head);
		resMessage.setBody(body);
		return resMessage;
	}

	private Head generateResHeadMessage(Document document, XPath xpath, Head head) throws Exception {
		// 获取节点值
		String version = (String) xpath.evaluate("/message/head/version/text()", document, XPathConstants.STRING);
		String msgType = (String) xpath.evaluate("/message/head/msgType/text()", document, XPathConstants.STRING);
		String smzfMsgId = (String) xpath.evaluate("/message/head/smzfMsgId/text()", document, XPathConstants.STRING);
		String reqDate = (String) xpath.evaluate("/message/head/reqDate/text()", document, XPathConstants.STRING);
		String respDate = (String) xpath.evaluate("/message/head/respDate/text()", document, XPathConstants.STRING);
		String respType = (String) xpath.evaluate("/message/head/respType/text()", document, XPathConstants.STRING);
		String respCode = (String) xpath.evaluate("/message/head/respCode/text()", document, XPathConstants.STRING);
		String respMsg = (String) xpath.evaluate("/message/head/respMsg/text()", document, XPathConstants.STRING);
		head.setVersion(version);
		head.setMsgType(msgType);
		head.setReqDate(reqDate);
		head.setRespCode(respCode);
		head.setRespDate(respDate);
		head.setRespMsg(respMsg);
		head.setRespType(respType);
		head.setSmzfMsgId(smzfMsgId);
		return head;
	}

	private void notifyBank(HttpServletResponse response) {
		try {
			response.setContentType("text/html;charset=GBK");
			// 通知民生银行成功接收银行通知
			String notifyMsg = "000000";
			logger.info("notifyBank send content: \n[" + notifyMsg + "]");
			response.setContentLength(notifyMsg.getBytes().length);
			response.getOutputStream().write(notifyMsg.getBytes());
			response.getOutputStream().close();
		} catch (IOException e) {
			logger.info(e.getMessage());
		}
	}
}
